package com.ep.thirdPlatformProject.system.util;

import cn.hutool.core.util.ObjectUtil;
import com.github.benmanes.caffeine.cache.AsyncCache;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;

@Slf4j
public class CaffeineCacheUtils {

    private static volatile Cache<Object, Object> syncCache;
    private static AsyncCache<Object, Object> asyncCache;

    private CaffeineCacheUtils() {

    }

    static {
        initCache();
    }


    public static void initCache() {
        // 初始化cache
        YamlPropertiesFactoryBean yamlProFb = new YamlPropertiesFactoryBean();
        yamlProFb.setResources(new org.springframework.core.io.ClassPathResource("application.yml"));
        Properties properties = Objects.requireNonNull(yamlProFb.getObject());
        int initialCapacity = Integer.parseInt(properties.getProperty("caffeine.initialCapacity"));
        int maximumSize = Integer.parseInt(properties.getProperty("caffeine.maximumSize"));
        int expireAfterWrite = Integer.parseInt(properties.getProperty("caffeine.expireAfterWriteMinutes"));
        if (ObjectUtil.isNull(syncCache)) {
            synchronized (CaffeineCacheUtils.class) {
                if (ObjectUtil.isNull(syncCache)) {
                    syncCache = Caffeine.newBuilder()
                            .initialCapacity(initialCapacity)
                            .maximumSize(maximumSize)
                            .expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES)
                            .build();
                }
            }
        }
        asyncCache = Caffeine.newBuilder()
                .initialCapacity(initialCapacity)
                .maximumSize(maximumSize)
                .expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES)
                .buildAsync();
    }

    public static void putSync(Object key, Object value) {
        syncCache.put(key, value);
    }

    public static Object getSync(Object key) {
        return syncCache.getIfPresent(key);
    }


    /**
     * 得到同步
     *
     * @param key   关键
     * @param clazz clazz
     * @return {@link T}
     */
    public static <T> T getSync(Object key, Class<T> clazz) {
        // 参数校验
        if (key == null || clazz == null) {
            throw new IllegalArgumentException("Key and Class cannot be null.");
        }

        Object object = syncCache.getIfPresent(key);

        if (ObjectUtil.isNull(object)) {
            return null;
        }

        try {
            if (clazz.isInstance(object)) {
                return clazz.cast(object);
            } else {
                // 增加object原始类型的日志记录
                log.error("类型转换异常! 原始类型为{}，异常的object: {}, 异常的clazz:{}", object.getClass().getName(), object, clazz);
                throw new TypeTransformException("类型转换异常! 原始类型为" + object.getClass().getName() + "，无法转换为" + clazz.getName());
            }
        } catch (ClassCastException e) {
            // 捕获并处理ClassCastException
            log.error("捕获到ClassCastException，key: {}, 尝试转换的类型: {}", key, clazz, e);
            throw new TypeTransformException("类型转换异常! 无法将key为" + key + "的值转换为" + clazz.getName(), e);
        }
    }


    public static CompletableFuture<Object> getAsync(Object key, Executor executor, Supplier<Object> supplier) {
        return asyncCache.get(key, k -> CompletableFuture.supplyAsync(() -> supplier, executor));
    }

    public static String get(String key, Function<Object, String> function) {
        return (String) syncCache.get(key, function);
    }

    public static void removeSync(Object key) {
        syncCache.invalidate(key);
    }


    public static void clearSync() {
        syncCache.invalidateAll();
    }

    static class TypeTransformException extends RuntimeException {
        public TypeTransformException(String message) {
            super(message);
        }

        public TypeTransformException(String message, Throwable cause) {
            super(message, cause);
        }
    }

}
